home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 11 / Cream of the Crop 11-1.iso / comm / ytsg3.zip / FMFMT.C < prev    next >
Text File  |  1991-12-03  |  32KB  |  744 lines

  1. /* This may turn out to be a callable utility for programs that need to        *
  2. *  find one or more files that match an ambiguous file spec.                   *
  3. *                                                                              *
  4. *  This code is thread-tolerant, and should be used serially.  In a program    *
  5. *  with multiple threads, use the following calling sequence:                  *
  6. *                                                                              *
  7. *                                                                              *
  8. *  There are three entry points:                                               *
  9. *      int fmf_init(char *pattern, unsigned mask, int mode)                                   *
  10. *                              which initializes a search for the calling      *
  11. *                              thread.  The mode is 0 for specified directory  *
  12. *                              only, or 1 for search-everything-below.         *
  13. *                                                                              *
  14. *      int fmf_return_next( char *where_to_put_name, int *where_put_attribute )*
  15. *                              which returns a pointer to a file name and  its *
  16. *                              attributes that satisfies the pattern.  The     *
  17. *                              returned is qualified relative to the name and  *
  18. *                              path specified in the pattern.  For example, if *
  19. *                              the pattern was "C:\OS2\*.EXE then the first    *
  20. *                              name returned might be "C:\OS2\ANSI.EXE"; a     *
  21. *                              later one might be                              *
  22. *                              "C:\OS2\INSTALL\CIKSTART.EXE"                   *
  23. *                                                                              *
  24. *      int fmf_query_max_threads()                                             *
  25. *                              which returns the maximum number of concurrent  *
  26. *                              threads this routine is willing to handle.      *
  27. *                                                                              *
  28. *      fmf_close()             which frees resources reserved for the calling  *
  29. *                              thread.  It doesn't have to be used, but if     *
  30. *                              the caller does a lot of thread starting and    *
  31. *                              stopping, he'd better use this when he kills    *
  32. *                              his thread.                                     *
  33. */
  34.  
  35.  
  36. #include <stdlib.h>
  37. #include <stdio.h>
  38. #include <io.h>
  39. #include <string.h>
  40. #define INCL_BASE
  41. #include <os2.h>
  42.  
  43. #define ALLF "*.*"
  44. #define chBACKSLASH  '\\'
  45. #define achBACKSLASH "\\"
  46. #define chCOLON      ':'
  47. #define chDOT        '.'
  48. #define achDOT       "."
  49. #define achDOTDOT    ".."
  50. #define YES  1
  51. #define NO   0
  52.                               /*----------------------------------------------*/
  53.                               /* Declare how many concurrent threads we are   */
  54.                               /* willing to handle                            */
  55.                               /*----------------------------------------------*/
  56. #define MAX_THREADS_HERE 12
  57.                               /*----------------------------------------------*/
  58.                               /* structure that describes the context         */
  59.                               /* information for each thread                  */
  60.                               /*----------------------------------------------*/
  61. typedef struct _context  {
  62.                   HDIR    dhandle;
  63.                   char    *path;
  64.                   struct _context *BackPointer;
  65.                 } CONTEXT;
  66.                               /*----------------------------------------------*/
  67.                               /*  Semephore to serialize access to globals    */
  68.                               /*----------------------------------------------*/
  69. ULONG   fmfSem = 0;
  70.                               /*----------------------------------------------*/
  71.                               /* variables global to fmf                      */
  72.                               /*----------------------------------------------*/
  73. int             ntids_here = 0;
  74. TID             threads_here[MAX_THREADS_HERE];
  75. CONTEXT *current_context[MAX_THREADS_HERE];
  76. char           *namespec[MAX_THREADS_HERE];
  77. char            drivespec[MAX_THREADS_HERE];
  78. int             mode[MAX_THREADS_HERE];
  79. unsigned        mask[MAX_THREADS_HERE];
  80. FILEFINDBUF     ffb;
  81. char            NameWkArea[CCHMAXPATH];
  82. char           *allfs = "*.*";
  83.                               /*----------------------------------------------*/
  84.                               /* The private routines                         */
  85.                               /*----------------------------------------------*/
  86. void AssembleName(int slot, char *where);
  87. void AssemblePath(int slot, char *where);
  88.  
  89. /*----------------------------------------------------------------------------*/
  90. /*    fmf_init                                                                */
  91. /*                                                                            */
  92. /* Find-matching-files initialization routine.  The user passes the pattern   */
  93. /* and the search mode (0 or 1).  This routine sets up the data areas needed  */
  94. /* to find and match the files.                                               */
  95. /*----------------------------------------------------------------------------*/
  96. int fmf_init(char *filespec, unsigned srchmask, int srchmode)
  97. {
  98.  
  99.        int     i, rc, ts, srchcnt, pathlen;
  100.        HDIR    handle1 = 1;
  101.        CONTEXT *cp;
  102.        char    *p, *fnstart, *pathstart;
  103.        PIDINFO pidinfo;
  104.  
  105. #ifdef DEBUG
  106. printf("fmf_init - into the routine\n");
  107. #endif
  108.                               /*----------------------------------------------*/
  109.                               /* Find out what thread called us               */
  110.                               /*----------------------------------------------*/
  111.        if ( (rc = DosGetPID(&pidinfo)) != NO_ERROR )
  112.          {  perror("fmf_init getPID");
  113.             return(rc);
  114.          }
  115. #ifdef DEBUG
  116. printf("fmf_init - thread id is %d\n", pidinfo.tid);
  117. #endif
  118.                               /*----------------------------------------------*/
  119.                               /* If there are no threads currently active,    */
  120.                               /* initialize the data areas                    */
  121.                               /*----------------------------------------------*/
  122.        DosSemWait(&fmfSem, -1);     /* serialize this section */
  123.        DosSemSet(&fmfSem);
  124.        ts = MAX_THREADS_HERE;
  125.        if (ntids_here == 0)
  126.          for (i = 0; i < MAX_THREADS_HERE ; i++)
  127.            {
  128.              threads_here[i] = 0;
  129.              current_context[i] = 0;
  130.              namespec[i] = 0;
  131.              drivespec[i] = '\0';
  132.              mode[i] = 0;
  133.              mask[i] = 0;
  134.            }
  135.                               /*----------------------------------------------*/
  136.                               /* If there are currently active threads, see   */
  137.                               /* if we are one of them.                       */
  138.                               /*----------------------------------------------*/
  139.        else
  140.          for  (i = 0; i < MAX_THREADS_HERE; i++)
  141.            if ( threads_here[i] == pidinfo.tid )
  142.              {
  143.                ts = i;
  144.                break;
  145.              }
  146.                               /*----------------------------------------------*/
  147.                               /* If this is the first call for our thread,    */
  148.                               /* try to find an empty slot for us.            */
  149.                               /*----------------------------------------------*/
  150.        if (ts == MAX_THREADS_HERE)
  151.          for(i = 0; i < MAX_THREADS_HERE; i++)
  152.            if (threads_here[i] == 0)
  153.                                  /*-------------------------------------------*/
  154.                                  /* If there is an empty slot, allocate memory*/
  155.                                  /* in which to keep our context information  */
  156.                                  /* and initialize the context                */
  157.                                  /*-------------------------------------------*/
  158.              {
  159.                threads_here[i] = pidinfo.tid;
  160.                ts = i;
  161.                ntids_here++;
  162.                if (!(cp = (CONTEXT *)malloc(sizeof(CONTEXT))) )
  163.                  {
  164.                     threads_here[i] = 0;
  165.                     ntids_here--;
  166.                     perror("fmf_init malloc of context");
  167.                     DosSemClear(&fmfSem);     /* Clear semaphone before exit */
  168.                     return(-2);
  169.                  }
  170.                current_context[i] = cp;
  171.                cp->BackPointer = 0;
  172.                cp->path = 0;
  173.                cp->dhandle = -1;
  174.                break;
  175.              }
  176.        DosSemClear(&fmfSem);     /* no need to serialize here */
  177.  
  178.        if (ts == MAX_THREADS_HERE)
  179.          {  perror("fmf_init No room for more threads");
  180.             return(-1);
  181.          }
  182. #ifdef DEBUG
  183. printf("The thread was placed in slot %d\n", ts);
  184. #endif
  185.                                  /*-------------------------------------------*/
  186.                                  /* There are a couple of quirks in filespecs */
  187.                                  /* that we need to worry about.  First, a    */
  188.                                  /* spec that consists only of the drive des- */
  189.                                  /* ignation must be modified to have a path; */
  190.                                  /* we do that by appending a dot.  Second,   */
  191.                                  /* a spec that ends in a backslash is impli- */
  192.                                  /* citly a directory, so we should append    */
  193.                                  /* *.*. Dot directories get a name of *.*,   */
  194.                                  /* too.                                      */
  195.                                  /*-------------------------------------------*/
  196.        mode[ts] = srchmode;
  197.        mask[ts] = srchmask | FILE_ARCHIVED;
  198.        cp = current_context[ts];
  199.        cp->dhandle = -1;
  200.        DosSemWait(&fmfSem, -1);     /* serialize this section */
  201.        DosSemSet(&fmfSem);
  202.        strcpy (NameWkArea, filespec); /* get the filespec where we can chg it*/
  203.        p = NameWkArea;
  204.        if (NameWkArea[1] == chCOLON) /* Is drive specified? If so, capture it */
  205.          {
  206.            drivespec[ts] = *p;
  207.            p += 2;
  208.          }
  209.        pathstart = p;
  210.        if (!*p)                   /* Was only the drive specified, or nothing?*/
  211.          strcat(NameWkArea, achDOT);   /* Put in the assumed dot */
  212.                                   /* If "\" terminates spec, add *.*          */
  213.        for (p = NameWkArea; *p; p++);
  214.        --p;
  215.        if (*p == chBACKSLASH)
  216.          strcat(NameWkArea, allfs);
  217.                                   /*-------------------------------------------*/
  218.                                  /* Do DosFindFirst on the path to make sure  */
  219.                                  /* it exists.                                */
  220.                                  /*-------------------------------------------*/
  221.        srchcnt = 1;
  222.        rc = DosFindFirst(NameWkArea,
  223.                          &handle1,
  224.                          FILE_SYSTEM | FILE_HIDDEN | FILE_DIRECTORY,
  225.                          &ffb,
  226.                          sizeof(FILEFINDBUF),
  227.                          &srchcnt,
  228.                          0l);
  229. #ifdef DEBUG
  230. printf("DosFindFirst on %s returned %d\n", filespec, rc);
  231. #endif
  232.        if ( rc != NO_ERROR)
  233.          {
  234.            DosSemClear(&fmfSem);      /* clear semaphore before exiting */
  235.            return(rc);
  236.          }
  237.                                  /*-------------------------------------------*/
  238.                                  /* Find the start of the name part of the    */
  239.                                  /* path specification.  It's either the part */
  240.                                  /* after the last backslash, the part after  */
  241.                                  /* the colon, or the whole thing.            */
  242.                                  /*-------------------------------------------*/
  243.  
  244.        if ( (p = strrchr(pathstart, chBACKSLASH)) )
  245.          fnstart = p + 1;
  246.        else
  247.          fnstart = pathstart;
  248. #ifdef DEBUG
  249. printf("Name within input spec is %s, in ffb %s\n", fnstart, ffb.achName);
  250. #endif
  251.                                  /*-------------------------------------------*/
  252.                                  /* If the path has been specified 'shorthand'*/
  253.                                  /* by just naming the directory, put a *.*   */
  254.                                  /* on the end.                               */
  255.                                  /*-------------------------------------------*/
  256.        if ((ffb.attrFile & FILE_DIRECTORY) && /* FindFirst yields a directory */
  257.            (stricmp(fnstart, ffb.achName)==0)) /*with same name as file sought*/
  258.          namespec[ts] = allfs;                   /* *.* implied              */
  259.        else                                     /* Just \ specified as path */
  260.          if ( (strcmp(pathstart, achBACKSLASH) == 0) ||
  261.               (*fnstart == chDOT) )             /* or a dot directory named */
  262.            namespec[ts] = allfs;                 /* Also implies *.*         */
  263.          else                                 /* A file object was specified */
  264.            {
  265.              if (strcmp(fnstart, allfs) == 0) /* If it wasn't *.* */
  266.                namespec[ts] = allfs;
  267.              else                             /*    allocate some space for it*/
  268.                if ( (namespec[ts] = (char *)malloc(strlen(fnstart) + 1)) )
  269.                  strcpy(namespec[ts], fnstart);
  270.                else
  271.                  {
  272.                    free(cp);
  273.                    current_context[ts] = 0;
  274.                    threads_here[i] = 0;
  275.                    drivespec[i] = '\0';
  276.                    ntids_here--;
  277.                    perror("fmf_init malloc of path");
  278.                    DosSemClear(&fmfSem);  /* clear semaphore before exiting */
  279.                    return(-2);
  280.                  }
  281.              if (fnstart - pathstart > 1)     /* back up to the backslash    */
  282.                --fnstart;                     /* unless it's the root        */
  283.              *fnstart = '\0';       /* and overlay it to mark end of path     */
  284.            }
  285.        pathlen = strlen(pathstart);
  286.        if (pathlen)
  287.          if ((cp->path = (char *)malloc(pathlen + 1)) )
  288.            strcpy(cp->path, pathstart);
  289.          else
  290.            {
  291.               if (namespec[ts] != allfs)
  292.                 free(namespec[ts]);
  293.               free(cp);
  294.               current_context[ts] = 0;
  295.               threads_here[i] = 0;
  296.               ntids_here--;
  297.               perror("fmf_init malloc of path");
  298.               DosSemClear(&fmfSem);     /* Clear semaphone before exit */
  299.               return(-2);
  300.            }
  301.        else
  302.          cp->path = 0;
  303.  
  304.  
  305.  
  306.  
  307.  
  308.        DosSemClear(&fmfSem);     /* no need to serialize here */
  309.  
  310. #ifdef DEBUG
  311. AssembleName(ts, NameWkArea);
  312. printf("Reassembled input file spec: %s\n", NameWkArea);
  313. #endif
  314.  
  315.        return(NO_ERROR);
  316. }
  317.  
  318.  
  319. /*----------------------------------------------------------------------------*/
  320. /*    fmf_query_max_threads                                                   */
  321. /*                                                                            */
  322. /* Returns an integer reporting the number of concurrently-active threads are */
  323. /* suported by the fmf routines.  An 'active' thread is one who has made an   */
  324. /* fmf_init() call since its last call to fmf_close().                        */
  325. /*----------------------------------------------------------------------------*/
  326. int fmf_query_max_threads()
  327. {
  328.    return(MAX_THREADS_HERE);
  329. }
  330.  
  331. /*----------------------------------------------------------------------------*/
  332. /*   fmf_close                                                                */
  333. /*                                                                            */
  334. /* Removes the current thread from the list of active threads.  Deallocates   */
  335. /* any resources devoted to context for the thread.  A thread that has issued */
  336. /* an fmf_init call should call this routine before it exits.                 */
  337. /*----------------------------------------------------------------------------*/
  338. void fmf_close()
  339. {
  340.        int i, rc;
  341.        CONTEXT *CurrContext, *NextContext;
  342.        PIDINFO pidinfo;
  343.  
  344.                               /*----------------------------------------------*/
  345.                               /* Find out what thread called us               */
  346.                               /*----------------------------------------------*/
  347.        if ( (rc = DosGetPID(&pidinfo)) != NO_ERROR )
  348.             perror("fmf_init getPID");
  349.        else
  350.          pidinfo.tid = 0;
  351. #ifdef DEBUG
  352. printf("fmf_close - thread id is %d\n", pidinfo.tid);
  353. #endif
  354.                               /*----------------------------------------------*/
  355.                               /* If that thread is among the active threads,  */
  356.                               /* deallocate resources devoted to the thread   */
  357.                               /* and take it off the active list.             */
  358.                               /*----------------------------------------------*/
  359.        for (i = 0; i < MAX_THREADS_HERE; i++)
  360.          if  (threads_here[i] == pidinfo.tid)
  361.            { CurrContext = current_context[i];
  362.              do
  363.                {
  364.                  if (namespec[i] != allfs)
  365.                    free(namespec[i]);
  366.                  drivespec[i] = '\0';
  367.                  if (CurrContext->path)
  368.                    free(CurrContext->path);
  369.                  NextContext = CurrContext->BackPointer;
  370.                  free(CurrContext);
  371.                  CurrContext = NextContext;
  372.                }    while (CurrContext);
  373.              current_context[i] = 0;
  374.              if (namespec[i])
  375.                if (namespec[i] != allfs)
  376.                  free(namespec[i]);
  377.              namespec[i] = 0;
  378.              threads_here[i] = 0;
  379.              DosSemWait(&fmfSem, -1);     /* serialize this section */
  380.              DosSemSet(&fmfSem);
  381.              ntids_here--;
  382.              DosSemClear(&fmfSem);     /* no need to serialize here */
  383.              break;
  384.            }
  385. }
  386.  
  387. /*----------------------------------------------------------------------------*/
  388. /*    fmf_return_next                                                         */
  389. /*                                                                            */
  390. /* Return a code of NO_ERROR, along with the name of the next file that       */
  391. /* matches the pattern specified in fmf_init; or return a code of             */
  392. /* ERROR_NO_MORE_FILES to indicate that there are no more files in the path   */
  393. /* that match the pattern; or return a code indicating an error occured.      */
  394. /*----------------------------------------------------------------------------*/
  395. int fmf_return_next(char *CopyNameTo, int *CopyAttrTo)
  396. {
  397.        CONTEXT *cp;
  398.        int i, rc, srchcnt;
  399.        PIDINFO pidinfo;
  400.        int fmfProcess(int indx, int *rc, char *NameTo, int *AttrTo);
  401.                               /*----------------------------------------------*/
  402.                               /* Find out what thread called us               */
  403.                               /*----------------------------------------------*/
  404.        if ( (rc = DosGetPID(&pidinfo)) != NO_ERROR )
  405.          {  perror("fmf_next getPID");
  406.             return(rc);
  407.          }
  408. #ifdef DEBUG
  409. printf("fmf_next - thread id is %d\n", pidinfo.tid);
  410. #endif
  411.                               /*----------------------------------------------*/
  412.                               /* If that thread is among the active threads,  */
  413.                               /* decide if a FindFirst has been done yet.  Do */
  414.                               /* a FindFirst or a FindNext accordingly.       */
  415.                               /* The routine fmfProcess returns               */
  416.                               /* YES if the work is done and we can return to */
  417.                               /* our caller; or NO to indicate we need to go  */
  418.                               /* through the while loop another time.         */
  419.                               /*----------------------------------------------*/
  420.        DosSemWait(&fmfSem, -1);     /* serialize this section */
  421.        DosSemSet(&fmfSem);
  422.        for (i = 0; i < MAX_THREADS_HERE; i++)
  423.          if  (threads_here[i] == pidinfo.tid)
  424.              while (1)
  425.                {
  426.                  cp = current_context[i];
  427.                  srchcnt = 1;
  428. #ifdef DEBUG
  429. printf("fmf_next: dhandle is %d\n", cp->dhandle);
  430. #endif
  431.  
  432.                  if (cp->dhandle == -1)              /* No FindFirst yet */
  433.                    {
  434.                    AssembleName(i, NameWkArea);
  435. #ifdef DEBUG
  436. printf("fnf_next FindFirst name is %s\n", NameWkArea);
  437. #endif
  438.                    rc = DosFindFirst(NameWkArea,
  439.                                      &(cp->dhandle),
  440.                                      mask[i] | FILE_DIRECTORY,
  441.                                      &ffb,
  442.                                      sizeof(FILEFINDBUF),
  443.                                      &srchcnt,
  444.                                      0l);
  445.                    }
  446.                  else                               /* FindFirst already done */
  447.                    rc = DosFindNext( cp->dhandle,
  448.                                      &ffb,
  449.                                      sizeof(FILEFINDBUF),
  450.                                      &srchcnt);
  451. #ifdef DEBUG
  452. printf("rc from DosFind* was %d\n", rc);
  453. #endif
  454.  
  455.                  if (fmfProcess(i, &rc, CopyNameTo, CopyAttrTo))
  456.                    {
  457.                      DosSemClear(&fmfSem);     /* Clear semephore before exit */
  458.                      return(rc);
  459.                    }
  460.                 }
  461.        DosSemClear(&fmfSem);     /* no need to serialize here */
  462.        return(NO_ERROR);
  463. }
  464.  
  465.  
  466.  
  467. /*----------------------------------------------------------------------------*/
  468. /*  fmfProcess                                                                */
  469. /*                                                                              */
  470. /* Handle the logic that required to decide whether to return a file name,    */
  471. /* to look for another one in the current search, or to start a new search    */
  472. /* context.  The logic is common after either a FindFirst or a FindNext       */
  473. /*----------------------------------------------------------------------------*/
  474.  
  475. int fmfProcess(int index, int *ReturnCode, char *PutFileName, int *PutAttr)
  476. {
  477.      CONTEXT *cp, *sBP, *newcp;
  478.      int     rc, pathlen, new_context;
  479.  
  480. #ifdef DEBUG
  481. printf("fmfProcess - into routine\n");
  482. #endif
  483.  
  484.      cp = current_context[index];
  485.      rc = *ReturnCode;   /* return code from DosFindFirst or DosFindNext */
  486.                               /*----------------------------------------------*/
  487.                               /* If a system error occured, or the path was   */
  488.                               /* bad, exit.                                   */
  489.                               /*----------------------------------------------*/
  490.      if (rc != NO_ERROR)
  491.        {
  492.          if (rc != ERROR_NO_MORE_FILES)
  493.            {
  494.              perror("fmf_next DosFindFirst");
  495.              *ReturnCode = rc;
  496.              return(YES);    /* we want the error returned to fmf caller */
  497.            }
  498. #ifdef DEBUG
  499. printf("fmfProcess - no more files. ");
  500. #endif
  501.                               /*----------------------------------------------*/
  502.                               /* If the path is good, but there are no        */
  503.                               /* matching file names, go back to previous     */
  504.                               /* context, or exit if we are on top level      */
  505.                               /*----------------------------------------------*/
  506.          DosFindClose(cp->dhandle);
  507.          if ((sBP = cp->BackPointer) != 0)  /* there is a higher context */
  508.            {
  509.              if (cp->path)
  510.                free(cp->path);
  511.              free(cp);
  512.              current_context[index] = sBP;
  513.              *ReturnCode = (NO_ERROR);
  514. #ifdef DEBUG
  515. printf("Restored previous context\n");
  516. #endif
  517.              return(NO);      /* we don't want to return to fmf caller */
  518.            }
  519.          else                               /* we're at the highest context */
  520.            {
  521.              cp->dhandle = -1;
  522.              *ReturnCode = rc;
  523. #ifdef DEBUG
  524. printf("Already at top-level context\n");
  525. #endif
  526.              return(YES);
  527.            }
  528.        }
  529.                               /*----------------------------------------------*/
  530.                               /* If DosFind*     completed successfully, look */
  531.                               /* at the file returned.                        */
  532.                               /*                                              */
  533.                               /* If the file found is a legitimate member of  */
  534.                               /* the family of files requested, check if it's */
  535.                               /* a directory.  If so, either ignore it or     */
  536.                               /* create a new context to handle it.           */
  537.                               /*----------------------------------------------*/
  538.      if ( (strcmp(ffb.achName, achDOT) == 0) ||
  539.           (strcmp(ffb.achName, achDOTDOT) == 0) )
  540.        {
  541.          *ReturnCode = NO_ERROR;
  542. #ifdef DEBUG
  543. printf("fmfProcess: Skipped dot directory %s\n", ffb.achName);
  544. #endif
  545.          return(NO);  /* we want to skip over dot directories */
  546.        }
  547.  
  548.      new_context = NO;
  549.      if ((ffb.attrFile & FILE_DIRECTORY))
  550.        if (mode[index] == 1)       /* We are walking subdirectories */
  551.          {                         /* create a new context */
  552.            if ( (newcp = (CONTEXT *)malloc(sizeof(CONTEXT))) == 0)
  553.              {  perror("fmf_process malloc of context");
  554.                 return(-2);
  555.              }
  556.  
  557.            newcp->dhandle = -1;
  558.            newcp->BackPointer = cp;
  559.            pathlen = 2;
  560.            if (cp->path)
  561.              pathlen += strlen(cp->path);
  562.            pathlen += strlen(ffb.achName);
  563.            if ( (newcp->path = (char *)malloc(pathlen)) == 0)
  564.              {
  565.               free(newcp);
  566.                perror("fmf_process malloc of path");
  567.                *ReturnCode = -2;
  568.                return(YES);
  569.              }
  570.            *(newcp->path) = '\0';
  571.            if (cp->path)
  572.              {
  573.                strcpy(newcp->path, cp->path);
  574.                if (strcmp(cp->path, achBACKSLASH) != 0)
  575.                  strcat(newcp->path, achBACKSLASH);
  576.              }
  577.            strcat(newcp->path, ffb.achName);
  578.            current_context[index] = newcp;
  579.            new_context = YES;
  580. #ifdef DEBUG
  581. printf("fmfProcess: created new context, path = %s\n", newcp->path);
  582. #endif
  583.          }
  584.                               /*----------------------------------------------*/
  585.                               /* If it the attributes indicate it's one on the*/
  586.                               /* files the caller wants to see, return it     */
  587.                               /*----------------------------------------------*/
  588. #ifdef DEBUG
  589. printf("Search mask: %x  attribute: %x\n", mask[index], ffb.attrFile);
  590. #endif
  591.      *ReturnCode = NO_ERROR;
  592.      if (mask[index] & ffb.attrFile)
  593.        {
  594.          AssemblePath(index, PutFileName);
  595.          if (!new_context)
  596.            {
  597.              if (cp->path)
  598.                if (strcmp(cp->path, achBACKSLASH) != 0)
  599.                  strcat(PutFileName, achBACKSLASH);
  600.              strcat(PutFileName, ffb.achName);
  601.            }
  602.          *PutAttr = ffb.attrFile;
  603.          return(YES);
  604.        }
  605.      else
  606.        {
  607.          return(NO);
  608. #ifdef DEBUG
  609. printf("fmfProcess: Skipped %s %s\n", (ffb.attrFile & FILE_DIRECTORY) ?
  610.                                            "Subdirectory" : "File",
  611.                                        ffb.achName);
  612. #endif
  613.        }
  614.      return(YES);
  615. }
  616.  
  617.  
  618. /*----------------------------------------------------------------------------*/
  619. /* private routine to assemble a proper file name                             */
  620. /*----------------------------------------------------------------------------*/
  621. void AssembleName(int slot, char *where)
  622. {
  623.   CONTEXT *cp;
  624.  
  625.   cp = current_context[slot];
  626.   AssemblePath(slot, where);
  627.   if ( (cp->path) && (strcmp(cp->path, achBACKSLASH) != 0) )
  628.     strcat (where, achBACKSLASH);
  629.   strcat(where, namespec[slot]);
  630. }
  631.  
  632. /*----------------------------------------------------------------------------*/
  633. /* private routine to assemble a proper path name                             */
  634. /*----------------------------------------------------------------------------*/
  635. void AssemblePath(int slot, char *where)
  636. {
  637.   char *p;
  638.  
  639.   p = where;
  640.   *p = '\0';
  641.   if (drivespec[slot])
  642.     {
  643.       *p++ = drivespec[slot];
  644.       *p++ = chCOLON;
  645.     }
  646.   if (current_context[slot]->path)
  647.     strcpy(p, current_context[slot]->path);
  648.   else
  649.     *p = '\0';
  650. }
  651.  
  652. #ifdef DBGMAIN
  653.  
  654. #define FSFTEST
  655. #include "fmf.h"
  656. #include <process.h>
  657. #include <stddef.h>
  658. #define STACKSIZE 4096
  659. ULONG ctrSem = 0;
  660. int   ctr = 0;
  661.  
  662.  
  663. main(argc, argv, envp)
  664.    int argc;
  665.    char *argv[];
  666.    char *envp[];
  667. {
  668.  
  669.    int attr, wkctr, threads, maxthreads, i, rc;
  670.    void dir(char *spec);
  671.    char name[CCHMAXPATH];
  672.  
  673.    if (argc < 2)
  674.      {
  675.       printf("\nTest scaffold for FindMatchingFile routines\n");
  676.       printf("invoke as foo filespec filespec filespec...\n");
  677.       return(0);
  678.      }
  679.  
  680.    maxthreads = fmf_query_max_threads();
  681.    printf("\nNumber of threads supported is %d.\n", maxthreads);
  682.  
  683.  
  684.    threads = 0;
  685.    for (i = 2; i < argc; i++)
  686.       if (_beginthread(dir, NULL, 4096, argv[i]) == -1)
  687.         perror("main - starting thread");
  688.       else
  689.         threads++;
  690.  
  691.    if ( (rc = fmf_init(argv[1], FILE_HIDDEN | FILE_DIRECTORY | FILE_SYSTEM, 1)
  692.                                                                   != NO_ERROR) )
  693.      printf("Thread 1: fmf_init failed, rc = %d\n", rc);
  694.    else
  695.      while (rc == NO_ERROR)
  696.        {
  697.          DosSleep(75L);
  698.          rc = fmf_return_next(name, &attr);
  699.          if (rc == NO_ERROR)
  700.            printf("Thread 1: %s\n", name);
  701.        }
  702.  
  703.    do
  704.      {
  705.        DosSemWait(&ctrSem, SEM_INDEFINITE_WAIT);
  706.        DosSemSet(&ctrSem);
  707.        wkctr = ctr;
  708.        DosSemClear(&ctrSem);
  709.        if (wkctr < threads)
  710.          DosSleep(500L);
  711.      } while (wkctr < threads);
  712.  
  713.    printf("Thread %d ending\n", *_threadid);
  714. }
  715.  
  716. void dir(char *spec)
  717. {
  718.    int tid, attr, rc;
  719.    char name[CCHMAXPATH];
  720.  
  721.    tid = *_threadid;
  722.    if ( (rc = fmf_init(spec, FILE_ARCHIVED, 0) != NO_ERROR) )
  723.      printf("Thread %d: fmf_init failed, rc = %d\n", tid, rc);
  724.    else
  725.      while (rc == NO_ERROR)
  726.        {
  727.          DosSleep(75L);
  728.          rc = fmf_return_next(name, &attr);
  729.          if (rc == NO_ERROR)
  730.            printf("Thread %d: %s\n", tid, name);
  731.        }
  732.  
  733.    if (tid != 1)
  734.      {
  735.       DosSemWait(&ctrSem, SEM_INDEFINITE_WAIT);
  736.       DosSemSet(&ctrSem);
  737.       ctr++;
  738.       DosSemClear(&ctrSem);
  739.       printf("Thread %d ending\n", tid);
  740.      }
  741. }
  742.  
  743. #endif
  744.